package miui.xmpp.impl;

import android.util.Log;

import java.net.SocketAddress;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerAdapter;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.ChannelPromise;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;
import miui.xmpp.XmppServer;
import miui.xmpp.XmppServerListener;
import miui.xmpp.message.Iq;
import miui.xmpp.message.Message;
import miui.xmpp.util.WifiUtil;

/**
 * Created by ouyang on 15-8-24.
 */
public class XmppServerImpl implements XmppServer {

    private static final String TAG = "XmppServerImpl";

    private boolean started;
    private XmppServerListener listener = null;
    private int port;

    private EventLoopGroup bossGroup;
    private EventLoopGroup workerGroup;
    private ChannelFuture f;

    public XmppServerImpl(XmppServerListener listener, int port) {
        this.listener = listener;
        this.port = port;
    }

    @Override
    public synchronized boolean start() {
        boolean ret = false;

        do {
            if (started) {
                break;
            }

            bossGroup = new NioEventLoopGroup(1);
            workerGroup = new NioEventLoopGroup();

            ServerBootstrap b = new ServerBootstrap();
            b.group(bossGroup, workerGroup)
                    .channel(NioServerSocketChannel.class)
                    .option(ChannelOption.SO_BACKLOG, 64)
                    .handler(new LoggingHandler(LogLevel.INFO))
                    .childHandler(new ChildChannelHandler());

            ret = true;
            started = true;

            try {
                f = b.bind(WifiUtil.getWifiInetAddress(), port).sync();
            } catch (InterruptedException e) {
                e.printStackTrace();
                ret = false;
                started = false;
            }

        } while (false);

        return ret;
    }

    @Override
    public synchronized boolean stop() {
        boolean ret = false;

        do {
            if (! started) {
                break;
            }

            ret = true;
            started = false;

            try {
                f.channel().close();
                f.channel().closeFuture().sync();
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                bossGroup.shutdownGracefully();
                workerGroup.shutdownGracefully();
            }
        } while (false);

        return ret;
    }

    @Override
    public int getListenPort() {
        return port;
    }

    @Override
    public boolean send(String ip, int port, Iq iq) {
        return false;
    }

    @Override
    public boolean send(String ip, int port, Message message) {
        return false;
    }

    @Override
    public boolean send(String ip, int port, String message) {
        return false;
    }

    private class ChildChannelHandler extends ChannelInitializer<SocketChannel> {
        @Override
        protected void initChannel(SocketChannel socketChannel) throws Exception {
            socketChannel.pipeline().addLast(new ServerHandler());
        }
    }

    @ChannelHandler.Sharable
    public class ServerHandler extends ChannelHandlerAdapter {

        @Override
        public void connect(ChannelHandlerContext ctx, SocketAddress remoteAddress, SocketAddress localAddress, ChannelPromise promise) throws Exception {
            Log.d(TAG, "connect");
            super.connect(ctx, remoteAddress, localAddress, promise);
        }

        @Override
        public void disconnect(ChannelHandlerContext ctx, ChannelPromise promise) throws Exception {
            Log.d(TAG, "disconnect");
            super.disconnect(ctx, promise);
        }

        @Override
        public void channelRead(ChannelHandlerContext ctx, Object msg) {
            Log.d(TAG, "channelRead: " + msg);

            // ctx.write(msg);
        }

        @Override
        public void channelReadComplete(ChannelHandlerContext ctx) {
            Log.d(TAG, "channelReadComplete");
            ctx.flush();
        }

        @Override
        public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
            // Close the connection when an exception is raised.
            cause.printStackTrace();
            ctx.close();
        }
    }
}
